/**
 * @file      wiota_app.c
 * @brief
 * @version   1.0
 * @date      2023.12.14
 *
 * @copyright Copyright (c) 2018 UCchip Technology Co.,Ltd. All rights reserved.
 *
 * Change Logs:
 * Date          Author       Notes
 * 2023.12.14    ucchip       the first version
 *
 */

#include "wiota_app.h"
#include "auth_manager.h"
#include "config_api.h"
#include "gateway_manager.h"
#include "interaction_manager.h"
#include "led_manager.h"
#include "manager_queue.h"
#include "manager_queue_comm.h"
#include "module_queue_struct.h"
#include "roll_call_manager.h"
#include "state_manager.h"
#include "time_slot_manager.h"
#include "uc_wiota_api.h"
#include "userid_manager.h"
#include "watchdog_app.h"
#include "wiota_flashing.h"
#include "wiota_freq.h"
#ifdef WIOTA_SUBF_MODE_SUPPORT
#include "wiota_subf.h"
#endif
#include <rthw.h>
#include <rtthread.h>
#define DBG_ENABLE
#define DBG_SECTION_NAME "wiota"
#include "uc_log.h"

// WIoTa最大连续发送失败次数
#define WIOTA_CONTINUE_SEND_FAIL_CNT (10)
// WIoTa状态检测周期，30秒
#define WIOTA_STATE_CHECK_PERIOD (30 * 1000)

// 初始缓存地址个数，当达到最大时判断缓存数据量是否达到WIOTA_DATA_CACHE_LEN，如果没有再申请WIOTA_DATA_ADDR_MAX个
#define WIOTA_DATA_ADDR_MAX (100)
// 最大缓存数据量50k
#define WIOTA_DATA_CACHE_LEN (50 * 1024)
// 缓存超时数据清理周期，30分钟
#define WIOTA_DATA_CLEAR_TIMEOUT (30 * 60 * 1000)

/**
 * @brief WIoTA数据管理结构体
 *
 */
typedef struct
{
    rt_mutex_t lock;            /**< 互斥锁 */
    unsigned int *data_addr;    /**< 缓存地址 */
    unsigned int data_addr_num; /**< 总的缓存地址个数 */
    unsigned int cur_cache_len; /**< 当前缓存数据量 */
    unsigned int min_data_idx;  /**< 最小缓存地址索引 */
    unsigned int add_cnt;       /**< 添加缓存数据个数 */
    unsigned int del_cnt;       /**< 删除缓存数据个数 */
} wiota_data_mng_t;

/**
 * @brief WIoTa APP线程状态枚举
 *
 */
typedef enum
{
    WIOTA_APP_EXIT = 0,     /**< 一般为OTA升级时线程退出 */
    WIOTA_APP_NORMAL = 1,   /**< WIoTa APP线程正常启动时的状态 */
    WIOTA_APP_ABNORMAL = 2, /**< WIoTa APP线程异常，一般为发送失败过多或周期检测状态异常 */
} wiota_app_state_e;

// WIoTa APP线程接收队列句柄
static void *g_wiota_app_queue_handler = RT_NULL;

// WIoTa发送超时时间，根据发送类型和symbol_length，按照最小速率下计算出来的最大值，保证WIoTa发送不会超时，导致发送失败
static const unsigned short g_send_timeout[2][4] = {{20, 40, 80, 160}, {40, 80, 160, 320}}; // unit sec

// WIoTa连续发送失败的次数，如果超过设定值，则认为信号异常或WIoTa协议栈异常，进入异常处理流程
static char g_continue_send_fail_cnt = 0;

// WIoTa状态检测定时器
static rt_timer_t g_check_time = RT_NULL;
// WIoTa数据管理
static wiota_data_mng_t g_wiota_data_mng = {0};

/**
 * @brief 根据发送类型返回发送超时时间
 *
 * @param type 0表示广播，1表示单播
 * @return 发送超时时间
 */
static unsigned int wiota_get_send_timeout(unsigned char type)
{
    unsigned char symbol_length = uc_static_get_wiota_sys_cfg()->symbol_length;

    return (g_send_timeout[type][symbol_length] * 1000);
}

/**
 * @brief WIoTA数据管理初始化
 *
 */
static void wiota_data_manager_init(void)
{
    rt_memset(&g_wiota_data_mng, 0, sizeof(wiota_data_mng_t));

    g_wiota_data_mng.data_addr = rt_malloc(WIOTA_DATA_ADDR_MAX * sizeof(unsigned int));
    RT_ASSERT(g_wiota_data_mng.data_addr);
    rt_memset(g_wiota_data_mng.data_addr, 0, WIOTA_DATA_ADDR_MAX * sizeof(unsigned int));
    g_wiota_data_mng.data_addr_num = WIOTA_DATA_ADDR_MAX;

    g_wiota_data_mng.lock = rt_mutex_create("data_lock", RT_IPC_FLAG_FIFO);
    RT_ASSERT(g_wiota_data_mng.lock);
}

/**
 * @brief WIoTA数据管理反初始化
 *
 */
static void wiota_data_manager_deinit(void)
{
    mng_to_wiota_data_t *dl_data = RT_NULL;

    for (int i = 0; i < g_wiota_data_mng.data_addr_num; i++)
    {
        dl_data = (mng_to_wiota_data_t *)(g_wiota_data_mng.data_addr[i]);

        if (dl_data)
        {
            manager_to_wiota_data_free(dl_data);
        }
    }

    if (g_wiota_data_mng.data_addr)
    {
        rt_free(g_wiota_data_mng.data_addr);
    }
    if (g_wiota_data_mng.lock)
    {
        rt_mutex_delete(g_wiota_data_mng.lock);
    }

    rt_memset(&g_wiota_data_mng, 0, sizeof(wiota_data_mng_t));
}

/**
 * @brief WIoTA数据管理，打印当前缓存数据量
 *
 */
int wiota_print_data_mng_cnt(void)
{
    TRACE_I("wiota_data_mng: add_cnt %u, del_cnt %u, cache_len %u",
            g_wiota_data_mng.add_cnt, g_wiota_data_mng.del_cnt, g_wiota_data_mng.cur_cache_len);
}

/**
 * @brief 计算diff_tick
 *
 */
unsigned int wiota_get_diff_tick(unsigned int tick1, unsigned int tick2)
{
    unsigned int diff_tick = 0;

    if (tick1 > tick2)
    {
        diff_tick = tick1 - tick2;
    }
    else
    {
        diff_tick = 0xFFFFFFFF - tick2 + tick1;
    }

    return diff_tick;
}

/**
 * @brief 超时数据检查，并清理超时数据
 *
 */
void wiota_timeout_data_check(void)
{
    mng_to_wiota_data_t *dl_data = RT_NULL;
    unsigned int cur_tick = 0;
    int clear_len = 0;

    wiota_print_data_mng_cnt();

    if (g_wiota_data_mng.cur_cache_len == 0)
    {
        return;
    }

    cur_tick = rt_tick_get();

    for (int i = 0; i < g_wiota_data_mng.data_addr_num; i++)
    {
        dl_data = (mng_to_wiota_data_t *)(g_wiota_data_mng.data_addr[i]);
        if (dl_data)
        {
            if (wiota_get_diff_tick(cur_tick, dl_data->data_tick) > WIOTA_DATA_CLEAR_TIMEOUT)
            {
                clear_len += dl_data->data_len;
                TRACE_I("wiota_data 0x%x clear len %u %u, tick %u %u", dl_data, dl_data->data_len, clear_len, cur_tick, dl_data->data_tick);
                g_wiota_data_mng.cur_cache_len -= dl_data->data_len;
                g_wiota_data_mng.del_cnt++;
                g_wiota_data_mng.data_addr[i] = 0;
                if (dl_data->data_idx < g_wiota_data_mng.min_data_idx)
                {
                    g_wiota_data_mng.min_data_idx = dl_data->data_idx;
                }
                manager_to_wiota_data_free(dl_data);
            }
        }
    }
}

/**
 * @brief 发送超时数据检查消息
 *
 */
void wiota_send_timeout_data_check_msg(void)
{
    module_queue_communication(g_wiota_app_queue_handler,
                               WIOTA_APP_MODULE,
                               WIOTA_APP_MODULE,
                               WIOTA_APP_TIMEOUT_DATA_CHECK,
                               RT_NULL);
}

/**
 * @brief WIoTA数据管理，解决AP重启后的内存泄漏问题
 *
 * @param data 数据指针
 * @return int 0表示数据可以发送，1表示数据缓存满了，不再发送
 */
int wiota_data_manager_add(mng_to_wiota_data_t *data)
{
    int index = 0;
    int result = 0;
    int is_add = 0;

    if (data == RT_NULL)
    {
        return 1;
    }

    rt_mutex_take(g_wiota_data_mng.lock, RT_WAITING_FOREVER);
    index = g_wiota_data_mng.min_data_idx;
    for (; index < g_wiota_data_mng.data_addr_num; index++)
    {
        if (g_wiota_data_mng.data_addr[index] == 0)
        {
            is_add = 1;
            break;
        }
    }
    if (index == g_wiota_data_mng.data_addr_num)
    {
        // 未达到缓存的最大数据量，再申请WIOTA_DATA_ADDR_MAX个地址空间
        if (g_wiota_data_mng.cur_cache_len < WIOTA_DATA_CACHE_LEN)
        {
            g_wiota_data_mng.data_addr = rt_realloc(g_wiota_data_mng.data_addr, (g_wiota_data_mng.data_addr_num + WIOTA_DATA_ADDR_MAX) * sizeof(unsigned int));
            RT_ASSERT(g_wiota_data_mng.data_addr);
            rt_memset(g_wiota_data_mng.data_addr + g_wiota_data_mng.data_addr_num, 0, WIOTA_DATA_ADDR_MAX * sizeof(unsigned int));
            g_wiota_data_mng.data_addr_num += WIOTA_DATA_ADDR_MAX;
            is_add = 1;
            TRACE_D("add_data realloc, num %u", g_wiota_data_mng.data_addr_num);
        }
        else
        {
            // 达到缓存的最大数据量，不再发送，直接释放
            manager_to_wiota_data_free(data);
            TRACE_W("add_data cache full, cache_len %u", g_wiota_data_mng.cur_cache_len);
            result = 2;
        }
    }

    if (is_add)
    {
        g_wiota_data_mng.data_addr[index] = (unsigned int)data;
        g_wiota_data_mng.cur_cache_len += data->data_len;
        g_wiota_data_mng.min_data_idx = index;
        g_wiota_data_mng.add_cnt++;
        data->data_idx = index;
        data->data_tick = rt_tick_get();
        data->data_cnt = g_wiota_data_mng.add_cnt;
        // TRACE_D("add_data idx %d, addr 0x%x, len %d, cnt %u", index, data, data->data_len, g_wiota_data_mng.add_cnt);
    }
    rt_mutex_release(g_wiota_data_mng.lock);

    return result;
}

/**
 * @brief WIoTA数据管理
 *
 * @param data 数据指针
 * @return int 0表示数据删除成功，1表示数据异常
 */
static int wiota_data_manager_del(mng_to_wiota_data_t *data)
{
    int res = 0;

    if (data == RT_NULL)
    {
        return 1;
    }

    rt_mutex_take(g_wiota_data_mng.lock, RT_WAITING_FOREVER);
    if (data->data_idx < g_wiota_data_mng.data_addr_num)
    {
        mng_to_wiota_data_t *dl_data = (mng_to_wiota_data_t *)(g_wiota_data_mng.data_addr[data->data_idx]);

        if (dl_data && dl_data == data)
        {
            if (g_wiota_data_mng.cur_cache_len >= data->data_len)
            {
                g_wiota_data_mng.cur_cache_len -= data->data_len;
                g_wiota_data_mng.del_cnt++;
                g_wiota_data_mng.data_addr[data->data_idx] = 0;
                if (data->data_idx < g_wiota_data_mng.min_data_idx)
                {
                    g_wiota_data_mng.min_data_idx = data->data_idx;
                }
                // TRACE_D("del_data idx %d, addr 0x%x, len %d, cnt %u", data->data_idx, data, data->data_len, g_wiota_data_mng.del_cnt);
            }
            else
            {
                TRACE_E("del_data err1, idx %d, len %d %d", data->data_idx, g_wiota_data_mng.cur_cache_len, data->data_len);
                res = 2;
            }
        }
        else
        {
            TRACE_E("del_data err2, idx %d, addr 0x%x 0x%x", data->data_idx, data, dl_data);
            res = 3;
        }
    }
    else
    {
        TRACE_E("del_data err3, idx %d, num %d", data->data_idx, g_wiota_data_mng.data_addr_num);
        res = 4;
    }
    rt_mutex_release(g_wiota_data_mng.lock);

    return res;
}

/**
 * @brief 创建WIoTa APP消息队列
 *
 * @return 0表示成功，非0表示失败
 */
int wiota_app_queue_create(void)
{
    g_wiota_app_queue_handler = manager_create_queue("wiota_app", 4, 40, UC_SYSTEM_IPC_FLAG_PRIO);
    if (g_wiota_app_queue_handler == RT_NULL)
    {
        TRACE_E("wiota_app_queue_create error");
        return 1;
    }

    return 0;
}

/**
 * @brief 获取WIoTa APP消息队列句柄，主要是网关管理线程使用
 *
 * @return 队列句柄
 */
void *get_wiota_app_queue(void)
{
    return g_wiota_app_queue_handler;
}

/**
 * @brief WIoTa APP初始化，如果时AT通信则初始化AT客户端，如果时SPI同行则初始化SPI相关
 *
 * @return 0表示成功，非0表示失败
 */
int wiota_app_init(void)
{
#ifdef UC_USING_AT_COM
    // AT客户端初始化
    if (RT_EOK != uc_wiota_at_client_init())
    {
        TRACE_E("uc_wiota_at_client_init fail");
        return 1;
    }
    TRACE_D("uc_wiota_at_client_init suc");

    // 发送一个"AT"指令，以确认AT通讯正常
    while (uc_wiota_at_client_connect(5000) != RT_EOK)
    {
        TRACE_E("uc_wiota_at_client_connect fail");
        rt_thread_mdelay(1000);
    }
    TRACE_I("uc_wiota_at_client_connect ok");
#elif defined(UC_USING_SPI_COM)
    uc_wiota_spi_com_init();
    TRACE_I("uc_wiota_spi_com_init ok");
#endif

    return 0;
}

/**
 * @brief 定时器回调函数，发送状态检测消息给自身线程，用于周期检查WIoTa状态
 *
 * @param para 定时器创建时传入的参数
 */
static void wiota_state_check_func(void *para)
{
    module_queue_communication(g_wiota_app_queue_handler,
                               WIOTA_APP_MODULE,
                               WIOTA_APP_MODULE,
                               WIOTA_APP_CHECK_STATE,
                               RT_NULL);
}

/**
 * @brief WIoTa接收数据回调函数，将收到的数据发给网关管理线程处理
 *
 * @param recv_detail 上报数据详细信息
 */
static void wiota_recv_data_callback(unsigned int user_id, unsigned char *recv_data, unsigned short data_len, uc_recv_data_type_e data_type)
{
    if (data_type != DATA_TYPE_SUBF_DATA)
    {
        wiota_to_mng_data_t *wiota_info = rt_malloc(sizeof(wiota_to_mng_data_t));
        if (RT_NULL == wiota_info)
        {
            TRACE_E("wiota_info malloc fail");
            rt_free(recv_data);
            return;
        }

        wiota_info->id = user_id;
        wiota_info->data_len = data_len;
        wiota_info->data_type = data_type;
        wiota_info->data = (char *)recv_data;

        if (0 != module_queue_communication(module_get_queue(GATEWAY_MANAGER_MODULE),
                                            WIOTA_APP_MODULE,
                                            GATEWAY_MANAGER_MODULE,
                                            WIOTA_RECV_DATA,
                                            (void *)wiota_info))
        {
            TRACE_E("wiota_recv_data send queue error");
            rt_free(wiota_info);
            rt_free(recv_data);
        }
    }
}

/**
 * @brief WIoTa详细信息接收数据回调函数，将收到的数据发给网关管理线程处理
 *
 * @param recv_detail 上报数据详细信息
 */
static void wiota_recv_data_detail_callback(uc_recv_detail_t *recv_detail)
{
    if (recv_detail->data_type != DATA_TYPE_SUBF_DATA)
    {
        wiota_to_mng_data_t *wiota_info = rt_malloc(sizeof(wiota_to_mng_data_t));
        if (RT_NULL == wiota_info)
        {
            TRACE_E("wiota_info malloc fail");
            rt_free(recv_detail->data);
            return;
        }

        wiota_info->id = recv_detail->user_id;
        wiota_info->data_len = recv_detail->data_len;
        wiota_info->delay = recv_detail->delay;
        wiota_info->data_type = recv_detail->data_type;
        wiota_info->data = (char *)recv_detail->data;

        if (0 != module_queue_communication(module_get_queue(GATEWAY_MANAGER_MODULE),
                                            WIOTA_APP_MODULE,
                                            GATEWAY_MANAGER_MODULE,
                                            WIOTA_RECV_DATA,
                                            (void *)wiota_info))
        {
            TRACE_E("wiota_recv_data send queue error");
            rt_free(wiota_info);
            rt_free(recv_detail->data);
        }
    }
#ifdef WIOTA_SUBF_MODE_SUPPORT
    else
    {
#if 0 // 本地回环测试
        static unsigned int loop_cnt = 0;
        loop_cnt++;
        rt_kprintf("wiota_recv_voice_data len %d, loop_cnt %d\n", recv_detail->data_len, loop_cnt);
        // for (int i = 0; i < data_len; i++)
        // {
        //     rt_kprintf("%02x ", recv_detail->data[i]);
        // }
        // rt_kprintf("\n");

        audio_data_t *data_msg = (audio_data_t *)rt_malloc(sizeof(audio_data_t) + recv_detail->data_len);
        RT_ASSERT(data_msg);

        data_msg->dev_id = 0;
        data_msg->data_len = recv_detail->data_len;
        rt_memcpy(data_msg->data, recv_data, data_msg->data_len);

        if (RT_EOK != module_queue_communication(module_get_queue(WIOTA_APP_MODULE),
                                                 WIOTA_APP_MODULE,
                                                 WIOTA_APP_MODULE,
                                                 WIOTA_APP_ADD_SUBF_DATA,
                                                 data_msg))
        {
            rt_free(data_msg);
        }
#else
        audio_data_t *audio_data = (audio_data_t *)rt_malloc(sizeof(audio_data_t) + recv_detail->data_len);
        RT_ASSERT(audio_data);
        rt_memset(audio_data, 0, sizeof(audio_data_t) + data_len);

        ul_subf_node_t *subf_node = wiota_subf_get_node(recv_detail->user_id);

        audio_data->user_number = wiota_get_user_number();
        audio_data->dev_id = subf_node->dev_id;
        audio_data->frame_count = recv_detail->fn_cnt;
        audio_data->data_len = recv_detail->data_len;
        audio_data->first_access = wiota_get_first_access();
        rt_memcpy(audio_data->data, recv_detail->data, audio_data->data_len);

        // 语音数据为裸数据，没有网关协议头，不需要解码，直接发送到语音处理模块
        if (0 != module_queue_communication(module_get_queue(AUDIO_APP_MODULE),
                                            WIOTA_APP_MODULE,
                                            AUDIO_APP_MODULE,
                                            AUDIO_DATA_RECV,
                                            (void *)audio_data))
        {
            TRACE_E("wiota_recv_voice_data send queue error");
            rt_free(audio_data);
        }
        wiota_reset_first_access();
#endif
        // 拷贝走了，直接释放
        rt_free(recv_detail->data);
    }
#endif
}

/**
 * @brief 接收sync paging睡眠控制消息的回调函数，当终端已sync paging方式睡眠之前会发一个ctrl消息给AP，AP收到后会上报一个帧号，表示该终端的睡眠帧号
 *        后续根据检测周期在该帧号去唤醒该终端
 *
 * @param user_id   发送同步paging控制消息的终端id
 * @param burst_idx 该终端id所在帧结构的burst位置
 * @param fn_index  该终端睡眠的帧号
 */
static void wiota_recv_sync_paging_ctrl_callback(unsigned int user_id, unsigned char burst_idx, unsigned int fn_index)
{
    manager_address_t *reserve_addr = manager_query_reserve_addr_by_wiota_id(user_id);
    if (RT_NULL == reserve_addr)
    {
        TRACE_W("illegal user_id 0x%x", user_id);
        return;
    }
    reserve_addr->fn_index = fn_index;

    TRACE_D("recv user_id 0x%x sync_paging ctrl, fn_index %u", user_id, fn_index);
}

/**
 * @brief 授时状态上报
 *
 * @param state 授时状态
 */
static void wiota_ts_callback(time_service_state_e state)
{
    TRACE_I("time_service state %d", state);
}

/**
 * @brief 授时状态上报
 *
 * @param ts_info 授时信息
 */
static void wiota_ts_info_callback(uc_ts_info_t *ts_info)
{
    time_t cur_time = ts_info->cur_time_s;

    if (ts_info->ts_state == TIME_SERVICE_INIT_END ||
        ts_info->ts_state == TIME_SERVICE_ALIGN_END)
    {
        TRACE_I("time_service suc, fh %u, fho %u, time %u %u",
                ts_info->frame_head, ts_info->frame_head_offset, ts_info->cur_time_s, ts_info->cur_time_us);
        if (ts_info->cur_time_s)
        {
#ifdef RT_USING_RTC
            rt_device_control(rt_device_find("rtc"), RT_DEVICE_CTRL_RTC_SET_TIME, &cur_time);
            TRACE_I("Sync local time from GPS: %s", ctime((const time_t *)&cur_time));
#endif /* RT_USING_RTC */
        }
        // if (ts_info->ts_state == TIME_SERVICE_INIT_END)
        // {
        //     // 是否要将第一次的授时信息和定位信息发到网络???
        // }
    }
}

/**
 * @brief WIoTa启动完成，创建WIoTa状态检测timer，并通知网关管理线程，并携带WIoTa版本信息
 *
 */
static void wiota_startup_complete(void)
{
    // 创建WIoTa状态检测定时器，并启动
    g_check_time = rt_timer_create("check_timer",
                                   wiota_state_check_func,
                                   RT_NULL,
                                   WIOTA_STATE_CHECK_PERIOD,
                                   RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
    RT_ASSERT(g_check_time);
    rt_timer_start(g_check_time);

    // 发送启动完成消息给网关管理线程，以完成ID管理的初始化
    wiota_version_t *wiota_version = rt_malloc(sizeof(wiota_version_t));
    RT_ASSERT(wiota_version);
    rt_memset(wiota_version, 0, sizeof(wiota_version_t));

    if (RT_EOK != uc_wiota_get_version(wiota_version->uc8088_version,
                                       wiota_version->git_info_8088,
                                       wiota_version->make_time_8088,
                                       RT_NULL,
                                       RT_NULL,
                                       RT_NULL,
                                       &wiota_version->cce_version))
    {
        TRACE_E("get wiota_version error");
        return;
    }

    module_queue_communication(module_get_queue(GATEWAY_MANAGER_MODULE),
                               WIOTA_APP_MODULE,
                               GATEWAY_MANAGER_MODULE,
                               WIOTA_START,
                               (void *)wiota_version);

    // 注册WIoTa上行数据接收回调函数，网关会通过AT urc回调的方式触发该回调函数，
    // 将回调注册放到WIOTA_START之后，防止ID管理为初始化便收到数据
    // 网关SPI通信默认使用detail接收回调，且只支持detail回调，AT通信默认使用普通接收回调，且可切换
#ifdef UC_USING_AT_COM                                              // AT通信两个都可用，但只生效一个
    uc_wiota_register_recv_data_callback(wiota_recv_data_callback); // 普通接收回调
#elif defined(UC_USING_SPI_COM)
    uc_wiota_register_recv_data_detail_callback(wiota_recv_data_detail_callback);
#endif
    uc_wiota_register_sync_paging_callback(wiota_recv_sync_paging_ctrl_callback);
}

/**
 * @brief 配置并启动WIoTa
 *
 * @param is_factory 0表示普通启动，1表示工厂模式启动
 * @return int 0表示成功，非0表示失败
 */
int wiota_app_run(int is_factory)
{
    sub_system_config_t config;
    unsigned char ts_mode = UC_TS_NONE;
    wiota_cfg_t *wiota_cfg = RT_NULL;
    slot_send_cfg_t *slot_send_cfg = RT_NULL;

    wiota_cfg = uc_static_get_wiota_cfg();
    // 开启帧边界校准功能和同步校准方式，必须在WIoTa初始化之前
    ts_mode = wiota_cfg->ts_cfg.ts_mode;
    TRACE_I("ts_mode %d", ts_mode);
    if (UC_TS_NONE != ts_mode)
    {
        uc_wiota_set_frame_boundary_align_func(1);
        uc_wiota_set_time_service_func(ts_mode - 1, 1);
        uc_wiota_set_time_service_cycle(wiota_cfg->ts_cfg.ts_period);
        // 网关默认使用info回调，SPI通信的只支持info回调，AT通信的可切换
        // 注意：如果不使用info回调，就无法使用GPS给网关同步世界时间了
        uc_wiota_register_time_service_info_callback(wiota_ts_info_callback);
    }

    // 初始化WIoTa
    if (0 != uc_wiota_init())
    {
        TRACE_E("uc_wiota_init fail, ap firmware may not flashing");
        return 1;
    }

    // 设置CRC限制为1，表示任意长度的下行数据都添加协议层的CRC校验，由于该值默认值为100，后续可通过获取该值来判断AP是否重启
    uc_wiota_set_crc(1);

    // 设置WIoTa子网配置
    uc_wiota_get_system_config(&config);

#if 1 // v1.3硬件问题，暂时限制最大功率为22，但静态数据不改
    signed char power = wiota_cfg->power_cfg.power;
    if (power > 22)
    {
        power = 22;
    }
    config.power = power;
#else
    config.power = wiota_cfg->power_cfg.power;
#endif
    config.symbol_length = wiota_cfg->sys_cfg.symbol_length;
    config.dlul_ratio = wiota_cfg->sys_cfg.dlul_ratio;
    config.bt_value = wiota_cfg->sys_cfg.bt_value;
    config.group_number = wiota_cfg->sys_cfg.group_number;
    config.subsystem_id = wiota_cfg->sys_cfg.subsystem_id;

    TRACE_I("power         %d", config.power);
    TRACE_I("id_len        %d", config.id_len);
    TRACE_I("symbol_length %d", config.symbol_length);
    TRACE_I("dlul_ratio    %d", config.dlul_ratio);
    TRACE_I("bt_value      %d", config.bt_value);
    TRACE_I("group_number  %d", config.group_number);
    TRACE_I("spectrum_idx  %d", config.spectrum_idx);
    TRACE_I("old_subsys_v  %d", config.old_subsys_v);
    TRACE_I("bitscb        %d", config.bitscb);
    TRACE_I("subsystem_id  0x%x", config.subsystem_id);

    uc_wiota_set_system_config(&config);

    // 设置WIoTa频点
    uc_wiota_set_freq_info(wiota_cfg->freq_cfg.current_freq);

    // 设置单播MCS
    uc_wiota_set_data_rate(UC_RATE_NORMAL, wiota_cfg->rate_cfg.unicast_mcs);

    // 中速模式
    if (wiota_cfg->rate_cfg.mid_mode_state)
    {
        uc_wiota_set_data_rate(UC_RATE_MID, wiota_cfg->rate_cfg.mid_mode_state);
    }

    // 高速模式
    if (wiota_cfg->rate_cfg.high_mode_state)
    {
        uc_wiota_set_data_rate(UC_RATE_HIGH, wiota_cfg->rate_cfg.high_mode_state);
    }

    // 基带crc类型
    if (wiota_cfg->rate_cfg.crc_type)
    {
        uc_wiota_set_data_rate(UC_RATE_CRC_TYPE, wiota_cfg->rate_cfg.unicast_mcs);
    }

    // 设置广播MCS
    uc_wiota_set_broadcast_mcs(wiota_cfg->rate_cfg.broadcast_mcs);

    // 设置WIoTA连接态时间
    uc_wiota_set_active_time(wiota_cfg->act_cfg.active_time);

    // 设置WIoTa广播帧发送周期
    slot_send_cfg = uc_static_get_slot_send_cfg();
    unsigned char gw_mode = uc_static_get_gateway_auth_cfg()->work_mode;

    if (gw_mode == UC_TRANSMISSION_TIME_SLOT_MODE && slot_send_cfg->data_transport_period)
    {
        // 分时模式下，关闭AP内部广播帧发送，由上层应用同步帧号给终端，解决分时模式下子帧6的冲突
        uc_wiota_set_broadcast_fn_cycle(0);
        // 开启分时模式一定要关闭bnack，两者不兼容，AP默认开启，需要关闭
        uc_wiota_set_bnack_func(0);
    }
    else
    {
        uc_wiota_set_broadcast_fn_cycle(slot_send_cfg->bc_frame_send_period);
    }
#ifdef WIOTA_SUBF_MODE_SUPPORT
    wiota_subf_mode_init();
#endif

    wiota_data_manager_init();

    if (!is_factory) // 工厂模式不需要延迟
    {
        // 延迟一点启动AP，保证连上的终端失步，触发重新鉴权流程
        int delay_time[4] = {5000, 5000, 15000, 25000};
        rt_thread_mdelay(delay_time[config.symbol_length]);
    }
    uc_wiota_run();
    rt_thread_mdelay(2000);

    // 不是同步助手授时则关闭电源
    if (ts_mode != UC_TS_SYNC_ASSISTANT)
    {
        uc_wiota_sync_assistant_power_off();
    }

    if (ts_mode != UC_TS_NONE)
    {
        // 开启授时，授时完成后的帧边界校准等操作均由AP独立完成
        uc_wiota_time_service_start();
        rt_thread_mdelay(1000);
    }

    return 0;
}

/**
 * @brief 延迟重发单播消息的工作项
 *
 * @param work      工作项
 * @param work_data 工作项数据
 */
static void wiota_app_msg_resend_work(struct rt_work *work, void *work_data)
{
    if (0 != module_queue_communication(get_wiota_app_queue(),
                                        WIOTA_APP_MODULE,
                                        WIOTA_APP_MODULE,
                                        WIOTA_APP_MSG,
                                        (void *)work_data))
    {
        manager_to_wiota_data_free((mng_to_wiota_data_t *)work_data);
    }

    rt_free(work);
}

/**
 * @brief WIOTA_APP_MSG发送结果处理，如果有重发会根据配置进行重发
 *
 * @param data    数据指针
 * @param user_id 终端ID
 * @param result  发送结果
 *
 * @return int 0表示不需要释放，1表示需要释放
 */
static int wiota_app_msg_result(mng_to_wiota_data_t *data, unsigned int user_id, unsigned int result)
{
    unsigned char uc_resend_times = uc_static_get_wiota_rate_cfg()->uc_resend_times;
    unsigned int resend_delay = manager_get_frame_len() / 1000 + 500;
    int is_free = 0;

    data->head.send_counter++;

    if (UC_OP_SUCC == result)
    {
        // TRACE_I("wiota_app_msg to 0x%x suc, cnt %d", user_id, data->head.send_counter);
        data->head.result = WIOTA_SEND_SUCCESS;
        g_continue_send_fail_cnt = 0;
        is_free = 1;
    }
    else
    {
        TRACE_W("wiota_app_msg to 0x%x failed %d, cnt %d", user_id, result, data->head.send_counter);
        data->head.result = WIOTA_SEND_FAIL;
        if (UC_OP_FAIL == result)
        {
            g_continue_send_fail_cnt++;
        }

        if (uc_resend_times > 0 && data->head.send_counter <= uc_resend_times)
        {
            struct rt_work *resend_work = rt_malloc(sizeof(struct rt_work));
            RT_ASSERT(resend_work);

            rt_work_init(resend_work, wiota_app_msg_resend_work, (void *)data);
            rt_work_submit(resend_work, resend_delay);
            TRACE_D("submit resend_work, delay %d", resend_delay);
        }
        else
        {
            // 不重发或已达最大重发次数
            is_free = 1;
        }
    }

    return is_free;
}

/**
 * @brief  设备状态上报
 *
 * @param  dev_id 设备id
 * @param  status 设备状态，0表示离线，1表示在线
 */
static void wiota_app_device_status_report(unsigned int dev_id, unsigned int status)
{
    device_status_t *dev_status = rt_malloc(sizeof(device_status_t));
    RT_ASSERT(dev_status);

    dev_status->dev_id = dev_id;
    dev_status->status = status;

    module_queue_communication(get_gateway_manager_queue(),
                               WIOTA_APP_MODULE,
                               GATEWAY_MANAGER_MODULE,
                               WIOTA_STATUS_REPORT,
                               (void *)dev_status);
}

/**
 * @brief 延迟重发点名消息的工作项
 *
 * @param work      工作项
 * @param work_data 工作项数据
 */
static void wiota_roll_call_msg_resend_work(struct rt_work *work, void *work_data)
{
    mng_to_wiota_data_t *data = (mng_to_wiota_data_t *)work_data;

    if (module_queue_communication(get_wiota_app_queue(),
                                   WIOTA_APP_MODULE,
                                   WIOTA_APP_MODULE,
                                   WIOTA_APP_SEND_ROLL_CALL,
                                   (void *)work_data))
    {
        manager_mark_dev_offline(data->head.id);
        manager_to_wiota_data_free((mng_to_wiota_data_t *)work_data);
    }

    rt_free(work);
}

/**
 * @brief WIOTA_APP_SEND_ROLL_CALL发送结果处理，如果有重发会固定重发两次，并进行发送结果统计和离线标记处理
 *
 * @param data    数据指针
 * @param user_id 终端ID
 * @param result  发送结果
 *
 * @return int 0表示不需要释放，1表示需要释放
 */
static int wiota_roll_call_result(mng_to_wiota_data_t *data, unsigned int user_id, unsigned int result)
{
    unsigned int rc_rto = uc_static_get_gateway_timed_cfg()->rc_rto;
    unsigned int resend_delay = manager_get_frame_len() / 1000 + 500;
    int is_free = 0;

    data->head.send_counter++;

    if (UC_OP_SUCC == result)
    {
        manager_roll_call_suc_num_inc();
        is_free = 1;
    }
    else
    {
        manager_roll_call_fail_num_inc();
        TRACE_W("roll_call_msg to 0x%x failed %d, cnt %d", user_id, result, data->head.send_counter);

        if (data->head.send_counter <= 2)
        {
            struct rt_work *rc_resend_work = rt_malloc(sizeof(struct rt_work));
            RT_ASSERT(rc_resend_work);

            rt_work_init(rc_resend_work, wiota_roll_call_msg_resend_work, (void *)data);
            rt_work_submit(rc_resend_work, resend_delay);
            TRACE_D("submit rc_resend_work, delay %d", resend_delay);
        }
        else
        {
            // 如果开启了通过点名来判断终端在线状态的功能，点名失败则认为该终端不在线，将其从标记为离线
            if (rc_rto)
            {
                manager_mark_dev_offline(user_id);
                wiota_app_device_status_report(manager_query_dev_id_by_wiota_id(user_id), 0);
            }
            is_free = 1;
        }
    }

    return is_free;
}

/**
 * @brief WIOTA_APP_AUTH_RES发送结果处理，发送成功后根据配置可能开启子帧模式
 *
 * @param user_id 终端ID
 * @param result  发送结果
 */
static void wiota_auth_result(unsigned int user_id, unsigned int result)
{
    if (UC_OP_SUCC == result)
    {
        g_continue_send_fail_cnt = 0;
#ifdef WIOTA_SUBF_MODE_SUPPORT // 鉴权响应发送成功后，如果子帧模式标志位为1，则开启子帧模式
        if (uc_static_get_wiota_subf_mode_cfg()->ul_subf_mode)
        {
            unsigned int *user_id = rt_malloc(sizeof(unsigned int));
            if (user_id)
            {
                *user_id = result->user_id;
                if (module_queue_communication(get_wiota_app_queue(),
                                               WIOTA_APP_MODULE,
                                               WIOTA_APP_MODULE,
                                               WIOTA_APP_SUBF_MODE,
                                               (void *)user_id))
                {
                    rt_free(user_id);
                }
            }
        }
#endif
        wiota_app_device_status_report(user_id, 1);
    }
    else
    {
        TRACE_W("auth_msg to 0x%x, result %d", user_id, result);
        if (UC_OP_FAIL == result)
        {
            g_continue_send_fail_cnt++;
        }
    }
}

/**
 * @brief 非阻塞单播发送回调函数，处理发送完成后的逻辑
 *
 * @param result 发送结果
 */
static void wiota_send_callback(uc_send_recv_t *result)
{
    mng_to_wiota_data_t *data = (mng_to_wiota_data_t *)result->data_id;
    int is_free = 0;

    if (0 != wiota_data_manager_del(data))
    {
        return;
    }

    switch (data->head.cmd)
    {
    case WIOTA_APP_MSG:
        is_free = wiota_app_msg_result(data, result->user_id, result->result);
        break;

    case WIOTA_APP_SEND_ROLL_CALL:
        is_free = wiota_roll_call_result(data, result->user_id, result->result);
        break;

    case WIOTA_APP_AUTH_RES:
        is_free = 1; // 没有重发，直接释放
        wiota_auth_result(result->user_id, result->result);
        break;

    case WIOTA_APP_RS_SM_BY_FN:
    {
        is_free = 1; // 没有重发，直接释放
        manager_cache_size_revert(data->data_len);
        if (result->result != UC_OP_SUCC)
        {
            TRACE_W("rs_fn to 0x%x fail", result->user_id);
        }
        break;
    }

    default:
        break;
    }

    if (is_free)
    {
        manager_to_wiota_data_free(data);
    }
}

/**
 * @brief 非阻塞OTA发送回调函数
 *
 * @param result
 */
static void wiota_send_ota_callback(uc_send_recv_t *result)
{
    if (UC_OP_SUCC == result->result)
    {
        g_continue_send_fail_cnt = 0;
    }
    else
    {
        TRACE_W("wiota_send_ota_callback result %d", result->result);
        if (UC_OP_FAIL == result->result)
        {
            g_continue_send_fail_cnt++;
        }
    }
}

static void wiota_sync_paging_cb(uc_paging_recv_t *result)
{
    TRACE_I("user_id 0x%x sync paging suc", result->user_id);
}

/**
 * @brief 定帧接收和发送，该接口必须在WIOTA_APP模块中的WIOTA_APP_RS_SM_BY_FN消息调用
 *
 */
void wiota_handle_sf_info_msg(sf_info_t *sf_info, uc_send_callback cb)
{
    recv_send_by_fn_t rs_fn = {0};
    mng_to_wiota_data_t *cache_data = RT_NULL;

    RT_ASSERT(sf_info->dev_num <= 16);
    for (int i = 0; i < sf_info->dev_num; i++)
    {
        // 接收
        rs_fn.user_id = sf_info->sf_send[i].wiota_id;
        rs_fn.start_recv_fn = sf_info->frame_num;
        rs_fn.recv_fns = sf_info->ul_recv_fns;
        rs_fn.send_fns = 0;
        rs_fn.data_len = 0;
        rs_fn.data = RT_NULL;
        rs_fn.callback = RT_NULL;
        rs_fn.para = RT_NULL;

        // 有缓存数据，则发送
        if (!rt_list_isempty(&sf_info->sf_send[i].data_list))
        {
            sf_data_node_t *data_node = rt_list_entry(sf_info->sf_send[i].data_list.next, sf_data_node_t, node);
            RT_ASSERT(data_node);
            cache_data = data_node->data;

            // 缓存检查，没缓存了只发送接收任务，不携带下行数据
            if (0 == wiota_data_manager_add(cache_data))
            {
                rt_list_remove(&data_node->node);
                rt_free(data_node);

                rs_fn.send_fns = sf_info->dl_send_fns;
                rs_fn.data_len = cache_data->data_len;
                rs_fn.data = cache_data->data;
                rs_fn.callback = cb;
                rs_fn.para = cache_data;
            }
        }

        // 安排定帧收发
        uc_wiota_recv_send_sm_by_fn(&rs_fn);
    }

    TRACE_D("rs_fn ts %03d, index %02d, dev_num %d, fn %u", sf_info->ts_fn, sf_info->ts_index, sf_info->dev_num, sf_info->frame_num);
}

/**
 * @brief 清除WIoTa APP线程数据，在WIoTa异常后，WIoTa APP线程将退出
 *
 */
static void wiota_app_task_exit(void)
{
    int res = 0;
    module_queue_message_t *page;

    rt_timer_stop(g_check_time);
    rt_timer_delete(g_check_time);
#ifdef WIOTA_SUBF_MODE_SUPPORT
    wiota_subf_mode_deinit();
#endif
    wiota_data_manager_deinit();
#ifdef UC_USING_SPI_COM
    uc_wiota_isr_cnt_clear();
#endif
    while (1)
    {
        res = manager_recv_queue(g_wiota_app_queue_handler, (void *)&page, 0);
        if (QUEUE_EOK != res)
            return;

        switch (page->cmd)
        {
        case WIOTA_APP_MSG:
        case WIOTA_APP_SEND_ROLL_CALL:
        case WIOTA_APP_AUTH_RES:
        case WIOTA_APP_SEND_BC:
        case WIOTA_APP_SEND_OTA:
        {
            mng_to_wiota_data_t *data = page->data;

            data->head.result = WIOTA_SEND_FAIL;
            manager_to_wiota_data_free(data);
            if (RT_NULL != page)
            {
                rt_free(page);
                page = RT_NULL;
            }
            break;
        }
        case WIOTA_APP_PAGING_TX:
        case WIOTA_APP_SYNC_PAGING:
        case WIOTA_APP_ADD_SUBF_DATA:
        case WIOTA_APP_CHECK_STATE:
        case WIOTA_APP_SUBF_MODE:
        case WIOTA_APP_OTA_UPGRADE:
        case WIOTA_APP_RESTART:
            if (RT_NULL != page)
            {
                if (RT_NULL != page->data)
                {
                    rt_free(page->data);
                    page->data = RT_NULL;
                }
                rt_free(page);
                page = RT_NULL;
            }
            break;

        case WIOTA_APP_RS_SM_BY_FN:
            // 该消息的page->data为全局变量地址，不能释放
            if (RT_NULL != page)
            {
                rt_free(page);
                page = RT_NULL;
            }
            break;

        default:
            break;
        }
    }
}

/**
 * @brief WIoTa APP任务处理函数，主要完成WIoTa的初始化和启动，以及处理网关管理线程发过来的下行消息处理等逻辑
 *
 * @param para 创建线程时传入的参数
 *
 */
static void wiota_handle_app_task_msg(void *para)
{
    module_queue_message_t *page = RT_NULL;
    wiota_app_state_e wiota_app_state = WIOTA_APP_NORMAL;

    while (WIOTA_APP_NORMAL == wiota_app_state)
    {
        if (QUEUE_EOK != manager_recv_queue(g_wiota_app_queue_handler, (void *)&page, UC_QUEUE_WAITING_FOREVER))
        {
            continue;
        }

        switch (page->cmd)
        {
        // 只能有一个callback，否则会出现发送接口里的全局回调函数指针被覆盖的情况，导致发送结果回来时调用callback出错，出现死机的问题，
        // 目前在数据里增加cmd便于区分不同类型消息的处理，并删除WIOTA_APP_NO_NOTIFY消息
        case WIOTA_APP_MSG:
        case WIOTA_APP_SEND_ROLL_CALL:
        case WIOTA_APP_AUTH_RES:
        {
            mng_to_wiota_data_t *dl_data = page->data;

            if (0 != wiota_data_manager_add(dl_data))
            {
                break;
            }

            if (WIOTA_APP_MSG == page->cmd)
            {
                TRACE_I("wiota WIOTA_APP_MSG");
            }
            else if (WIOTA_APP_SEND_ROLL_CALL == page->cmd)
            {
                TRACE_I("wiota WIOTA_APP_SEND_ROLL_CALL");
            }
            else if (WIOTA_APP_AUTH_RES == page->cmd)
            {
                TRACE_I("wiota WIOTA_APP_AUTH_RES");
            }

            if (UC_OP_SUCC != uc_wiota_send_data_order(dl_data->data,
                                                       dl_data->data_len,
                                                       dl_data->head.id,
                                                       wiota_get_send_timeout(1),
                                                       dl_data->order_business,
                                                       wiota_send_callback,
                                                       (void *)dl_data))
            {
                g_continue_send_fail_cnt++;
                TRACE_E("wiota cmd %d fail, data 0x%x", page->cmd, dl_data);
            }
            // 不能在这里free，因为发送需要时间，当结果返回时，该地址可能被另个消息申请，导致free出错
            break;
        }

        case WIOTA_APP_SEND_BC:
        case WIOTA_APP_SEND_OTA:
        case WIOTA_APP_SEND_BC_AUTH_TS:
        {
            mng_to_wiota_data_t *dl_data = page->data;
            uc_bc_mode_e bc_mode = OTA_BROADCAST;

            if (WIOTA_APP_SEND_BC == page->cmd)
            {
                TRACE_I("wiota WIOTA_APP_SEND_BC");
                bc_mode = NORMAL_BROADCAST;
            }
            else if (WIOTA_APP_SEND_OTA == page->cmd)
            {
                TRACE_I("wiota WIOTA_APP_SEND_OTA");
            }

            if (UC_OP_SUCC != uc_wiota_send_broadcast_data(dl_data->data,
                                                           dl_data->data_len,
                                                           bc_mode,
                                                           wiota_get_send_timeout(0),
                                                           wiota_send_ota_callback,
                                                           dl_data))
            {
                dl_data->head.result = WIOTA_SEND_FAIL;
                TRACE_E("WIOTA_APP_SEND_OTA fail, data 0x%x", dl_data);
                g_continue_send_fail_cnt++;
            }
            else
            {
                dl_data->head.result = WIOTA_SEND_SUCCESS;
            }

            // 广播消息可直接释放，因为回调没有相关处理逻辑
            manager_to_wiota_data_free(dl_data);
            break;
        }
#ifdef WIOTA_SUBF_MODE_SUPPORT
        case WIOTA_APP_ADD_SUBF_DATA:
        {
            audio_data_t *audio_data = (audio_data_t *)page->data;

            TRACE_I("wiota WIOTA_APP_ADD_SUBF_DATA");
            if (RT_EOK != uc_wiota_add_dl_subframe_data(audio_data->data, audio_data->data_len, audio_data->frame_count))
            {
                TRACE_E("WIOTA_APP_ADD_SUBF_DATA fail, data 0x%x", audio_data);
            }

            rt_free(audio_data);
            break;
        }
#endif

        case WIOTA_APP_PAGING_TX:
        {
            uc_lpm_tx_cfg_t *lpm_tx_cfg = uc_wiota_get_paging_tx_cfg();
            wiota_paging_tx_cfg_t *paging_tx_cfg = uc_static_get_wiota_paging_tx_cfg();

            TRACE_I("wiota WIOTA_APP_PAGING_TX");
            lpm_tx_cfg->freq = paging_tx_cfg->freq;
            lpm_tx_cfg->spectrum_idx = 3;
            lpm_tx_cfg->bandwidth = 1;
            lpm_tx_cfg->symbol_length = paging_tx_cfg->symbol_length;
            lpm_tx_cfg->awaken_id = paging_tx_cfg->awaken_id;
            lpm_tx_cfg->send_time = paging_tx_cfg->send_time;
            lpm_tx_cfg->mode = paging_tx_cfg->mode;

            if (RT_EOK == uc_wiota_set_paging_tx_cfg(lpm_tx_cfg))
            {
                if (RT_EOK == uc_wiota_start_paging_tx())
                {
                    TRACE_D("WIOTA_APP_PAGING_TX suc");
                }
            }
            break;
        }

        case WIOTA_APP_SYNC_PAGING:
        {
            uc_paging_info_t paging_info = {0};
            wiota_sync_paging_cfg_t *sync_paging_cfg = uc_static_get_wiota_sync_paging_cfg();
            manager_address_t *reserve_addr = manager_query_reserve_addr_by_dev_id(sync_paging_cfg->dev_id);

            TRACE_I("wiota WIOTA_APP_SYNC_PAGING");
            if (RT_NULL != reserve_addr)
            {
                paging_info.user_id = reserve_addr->wiota_id | (1 << 31);
                paging_info.fn_index = reserve_addr->fn_index;
                paging_info.detection_period = sync_paging_cfg->detection_period;
                if (reserve_addr->fn_index != reserve_addr->last_fn_index)
                {
                    paging_info.send_round = sync_paging_cfg->send_round;
                    paging_info.continue_fn = sync_paging_cfg->continue_fn;
                }
                else
                {
                    // 发送周期信号唤醒前没有收到终端上报的消息（fn_Index未更新），则改为连续帧发送
                    paging_info.send_round = 2;
                    paging_info.continue_fn = paging_info.detection_period / 2;
                }
                reserve_addr->last_fn_index = reserve_addr->fn_index;

                if (UC_OP_SUCC == uc_wiota_sync_paging(&paging_info, wiota_sync_paging_cb))
                {
                    TRACE_D("WIOTA_APP_SYNC_PAGING suc");
                }
            }
            else
            {
                TRACE_W("sync_paging illegal dev_id 0x%x", sync_paging_cfg->dev_id);
            }
            break;
        }

        case WIOTA_APP_RS_SM_BY_FN:
            wiota_handle_sf_info_msg((sf_info_t *)page->data, wiota_send_callback);
            // page->data为全局变量地址，不能free
            break;

#ifdef WIOTA_SUBF_MODE_SUPPORT
        case WIOTA_APP_SUBF_MODE: // 语音业务专用模式
        {
            unsigned int *user_id = page->data;
            manager_address_t *reserve_addr = manager_query_reserve_addr_by_dev_id(*user_id);
            unsigned char ul_subf_mode = uc_static_get_wiota_subf_mode_cfg()->ul_subf_mode;

            TRACE_I("wiota WIOTA_APP_SUBF_MODE");
            uc_wiota_set_ul_subframe_mode(ul_subf_mode, reserve_addr->wiota_id | (1 << 31), reserve_addr->rach_delay);
            rt_free(user_id);
            break;
        }
#endif

        case WIOTA_APP_CHECK_STATE:
        {
            TRACE_I("wiota WIOTA_APP_CHECK_STATE");

            // 如果处于扫频状态，则不处理任何消息
            if (manager_get_scan_freq_state())
            {
                TRACE_W("WIoTa is scanning freq, ignore check state!");
                break;
            }
#ifdef UC_USING_SPI_COM
            if (1 != uc_wiota_check_state())
            {
                TRACE_E("wiota_check_state error!!");
                wiota_app_state = WIOTA_APP_ABNORMAL;
            }
#elif defined(UC_USING_AT_COM)
            unsigned short crc_value = uc_wiota_get_crc();
            int ap_8288_state = uc_wiota_get_ap8288_state();

            if (crc_value != 1 || ap_8288_state == 0)
            {
                TRACE_E("wiota_check_state error!! crc %d 8288_state %d", crc_value, ap_8288_state);
                wiota_app_state = WIOTA_APP_ABNORMAL;
            }
            else
            {
                TRACE_I("wiota state ok");
            }
#endif
            break;
        }

        case WIOTA_APP_TIMEOUT_DATA_CHECK:
            wiota_timeout_data_check();
            break;

        case WIOTA_APP_OTA_UPGRADE:
            TRACE_I("wiota WIOTA_APP_OTA_UPGRADE");
            rt_timer_stop(g_check_time);
            uc_wiota_exit();
            wiota_app_state = WIOTA_APP_EXIT;
            break;

        case WIOTA_APP_RESTART:
            // 只重启WIoTa，重新走扫频流程，目的换一个频点
            wiota_app_state = WIOTA_APP_ABNORMAL;
            break;

        default:
            break;
        }

        if (RT_NULL != page)
        {
            rt_free(page);
            page = RT_NULL;
        }

        // 连续发送失败次数过多，主动检测AP是否异常，本身30s就检查一次，这里就不用再发消息检查了
        if (g_continue_send_fail_cnt > WIOTA_CONTINUE_SEND_FAIL_CNT)
        {
            TRACE_W("WIoTa continue send fail too many times, wait check!");
            g_continue_send_fail_cnt = 0;
        }
    }

    // 清理WIoTa APP线程数据
    wiota_app_task_exit();

    // 通知网关管理线程，WIoTa异常退出
    if (WIOTA_APP_ABNORMAL == wiota_app_state)
    {
        module_queue_communication(module_get_queue(GATEWAY_MANAGER_MODULE),
                                   WIOTA_APP_MODULE,
                                   GATEWAY_MANAGER_MODULE,
                                   WIOTA_ABNORMAL_EXIT,
                                   RT_NULL);
        // 回调置空
        uc_wiota_register_recv_data_callback(RT_NULL);
        uc_wiota_register_sync_paging_callback(RT_NULL);
        // WIoTa异常，重启AP
        uc_wiota_reboot();
        rt_thread_mdelay(500);
#ifdef UC_USING_SPI_COM
        uc_wiota_spi_com_deinit();
        uc_wiota_spi_com_init();
#endif
    }

    // 通知网关管理线程，WIoTa APP线程已退出，可进行OTA升级
    if (WIOTA_APP_EXIT == wiota_app_state)
    {
        module_queue_communication(module_get_queue(GATEWAY_MANAGER_MODULE),
                                   GATEWAY_MANAGER_MODULE,
                                   GATEWAY_MANAGER_MODULE,
                                   GATEWAY_REBOOT,
                                   RT_NULL);
        // 不在往下执行，等待重启
        while (1)
        {
            rt_thread_mdelay(1000);
        }
    }

    TRACE_W("WIoTa APP exit");
}

/**
 * @brief WIoTa APP线程处理函数
 *
 * @param para 创建线程时传入的参数
 */
void wiota_app_task(void *para)
{
    TRACE_I("wiota_app_task begin");

    // 检查AP和同步助手固件是否需要重刷
    if (0 != wiota_flashing())
    {
        rt_thread_mdelay(500);
        uc_wiota_reboot();
    }

    // WIoTa APP通信方式初始化
    if (0 != wiota_app_init())
    {
        return;
    }
    // 设置WIoTa状态灯初始状态为常亮
    send_led_event(LED_WIOTA, LED_E_EXCEPTION);

    while (1)
    {
        // 扫频和频点选择
        wiota_scan_freq();

        // 启动WIoTa
        if (0 != wiota_app_run(0))
        {
            rt_thread_mdelay(500);
            uc_wiota_reboot();
            continue;
        }

        // 通知网关管理线程，WIoTa协议栈启动完成
        wiota_startup_complete();

        wiota_handle_app_task_msg(para);
    }
}